﻿﻿using UnityEngine;  
using System.Collections;

public class Task  
{
    
    public bool Running {  
        get {  
            return task.Running;  
        }  
    }  
      
    public bool Paused {  
        get {  
            return task.Paused;  
        }  
    }

    public bool Locked
    {
        get => task.Locked;
        set => task.Locked = value;
    }
    
    public delegate void FinishedHandler(bool manual);  
      
    public event FinishedHandler Finished;  
    
    public Task(IEnumerator c, bool autoStart = true)  
    {  
        task = TaskManager.CreateTask(c);  
        task.Finished += TaskFinished;
        if(autoStart)  
            Start();  
    }
    
    public void Start()  
    {  
        task.Start();  
    }
    
    public void Stop()  
    {  
        task.Stop();
    }
      
    public void Pause()  
    {  
        task.Pause();  
    }
      
    public void Continue()  
    {  
        task.Unpause();  
    }
    
    void TaskFinished(bool manual)  
    {  
        FinishedHandler handler = Finished;  
        if(handler != null)  
            handler(manual);  
    }

    private readonly TaskManager.TaskState task;
}  
  
class TaskManager : MonoBehaviour  
{  
    public class TaskState
    {
        public bool Running => running;

        public bool Paused => paused;

        public bool Locked
        {
            get => locked;
            set => locked = value;
        }

        public delegate void FinishedHandler(bool manual);  
        public event FinishedHandler Finished;  
  
        private readonly IEnumerator coroutine;
        private bool running;
        private bool paused;
        private bool stopped;
        private bool locked;
          
        public TaskState(IEnumerator c)  
        {  
            coroutine = c;
        }  
          
        public void Pause()
        {  
            paused = true;
        }  
          
        public void Unpause()  
        {  
            paused = false;  
        }  
          
        public void Start()  
        {  
            running = true;  
            _singleton.StartCoroutine(CallWrapper());
        }  
          
        public void Stop()  
        {  
            if(locked)
                return;
            stopped = true;  
            running = false;
        }
          
        IEnumerator CallWrapper()
        {
            yield return null;
            IEnumerator e = coroutine;
            float pauseTime = 0;
            while (running)
            {
                if (paused)
                {
                    if (e != null && e.Current != null)
                    {
                        object yield = e.Current;
                        if (yield is WaitForSeconds)
                        {
                            pauseTime += Time.deltaTime;  //计算暂停的时间
                        }
                    }
                    yield return null;
                }
                else
                {
                    if (Mathf.Abs(pauseTime) > Mathf.Epsilon)
                    {
                        yield return new WaitForSeconds(pauseTime);
                        pauseTime = 0;
                    }
                    
                    if (e != null && e.MoveNext())
                    {
                        yield return e.Current;
                    }
                    else
                    {
                        running = false;
                    }
                }
            }

            FinishedHandler handler = Finished;
            if(handler != null)
                handler(stopped);
        }

    }

    private static TaskManager _singleton;

    public static TaskState CreateTask(IEnumerator coroutine)
    {  
        if(null == _singleton) {
            GameObject go = new GameObject("TaskManager");
            _singleton = go.AddComponent<TaskManager>();
            DontDestroyOnLoad(go);
        }
        return new TaskState(coroutine);
    }
} 
